package com.example.demofdfs.aop;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.example.demofdfs.model.BaseEntity;
import com.example.demofdfs.model.User;
import com.example.demofdfs.model.UserLog;
import com.example.demofdfs.service.UserLogService;
import com.example.demofdfs.service.UserService;

@Aspect
@Component
public class TestAop {

	@Autowired
	private UserLogService userLogService;
	@Autowired
	private UserService userService;
	
	// ProceedingJoinPoint point
	
	ThreadLocal<BaseEntity> threadLocal = new ThreadLocal<BaseEntity>();
	
	@Pointcut("execution(* com.example.demofdfs.service.impl.*.*(..))")
	public void pointcutAll() {}
	
	@Pointcut("update() || add()")
	private void pointcut() {}
	
	@Pointcut("execution(* com.example.demofdfs.service.impl.UserServiceImpl.update*(..))")
	private void update() {}
	
	@Pointcut("execution(* com.example.demofdfs.service.impl.UserServiceImpl.add*(..))")
	private void add() {}
	
//	@After("pointcut()")
	public void after(JoinPoint joinPoint) {
		
		BaseEntity baseEntity = threadLocal.get(); 
		Object[] args = joinPoint.getArgs();
		for (Object obj : args) {
			if (obj instanceof User) {
				User user = (User) obj;
				UserLog log = new UserLog();
				log.updateInfo(user.getId());
				userLogService.insert(log);
				
				System.out.println(Thread.currentThread().getName() + " : " + log.getId());
			}
		}
	}
	
//	@Before("pointcut()")
	public void before(JoinPoint joinPoint) {
		BaseEntity currentEntity;
		if (! (joinPoint.getArgs()[0] instanceof BaseEntity)) 
			return ;
		currentEntity = (BaseEntity) joinPoint.getArgs()[0];
		if (currentEntity.getId() == null) 
			return ;
		BaseEntity entity = userService.getUser(currentEntity.getId());
		if (entity != null)
			threadLocal.set(entity);
	}
	
	@Around("pointcutAll()")
	public Object around(ProceedingJoinPoint point) throws Throwable {
		
		if (! isRecording(point))
			return point.proceed();
		
//		String mothodName = point.getSignature().getName();
		
		Object[] args = point.getArgs();
		BaseEntity entity = null;
		for (Object obj : args)
			if (obj instanceof BaseEntity)
				entity = (BaseEntity) obj;
		
		if (entity == null)
			return point.proceed();
		
		Class<?> clazz = entity.getClass();
		Method[] Methods = clazz.getDeclaredMethods();
		Method test = null, tmp;
		for (Method m : Methods) {
			tmp = m;
			if (tmp.getName().equals("getTest"))
				System.out.println(m.invoke(entity));
		}
		
		System.out.println(clazz.getDeclaredField("FINAL_NAME").get(entity));	// 获取静态常量值
		
		Object obj = point.proceed(args);
		
		return obj;
	}
	
	private boolean isRecording(JoinPoint point) throws NoSuchMethodException, SecurityException {
		
		Object[] args = point.getArgs();
		Class<?> clazzs[] = new Class<?>[args.length];
		
		for (int i = 0; i < args.length; i++) {
			clazzs[i] = args[i].getClass();
		}
		
		Signature signature = point.getSignature();
		Class<?> clazz = signature.getDeclaringType();
		String methodName = signature.getName();
		
		Method method = clazz.getDeclaredMethod(methodName, clazzs);
		Annotation[] annotation =  method.getAnnotations();
		
		boolean flag = false;
		for (Annotation a : annotation) {
			Class<? extends Annotation> type = a.annotationType();
			if (type.equals(TestLogAnnotation.class))
				return true;
		}
		return flag;
	}
	
	
}
